package com.stox.client.ui.javafx.screener.widget;

import java.util.List;
import java.util.TimerTask;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;

import com.stox.client.api.core.ClientFactory;
import com.stox.client.api.data.provider.ScripClient;
import com.stox.client.api.screener.ScreenerClient;
import com.stox.client.ui.javafx.core.StoxPlatform;
import com.stox.client.ui.javafx.core.util.CSS;
import com.stox.client.ui.javafx.core.util.UI;
import com.stox.client.ui.javafx.widget.InformativeProgressBar;
import com.stox.shared.beans.BeanDescriptor;
import com.stox.shared.beans.PropertyDescriptor;
import com.stox.shared.i18n.MessageSource;
import com.stox.shared.model.ScreenBeanDescriptor;
import com.stox.shared.model.ScripWrapper;
import com.stox.shared.util.Constant;

public class ScanResultPanel extends VBox {

	private static final long DELAY = 500;
	private final InformativeProgressBar progressBar;
	private final Node progressInformationNode;
	private final ScripWrapperTableView scripWrapperTableView;
	private final TimerTask scanResultPollingTask;
	private final ScreenerClient screenerClient = ClientFactory.getClient(ScreenerClient.class);
	private final ScripClient scripClient = ClientFactory.getClient(ScripClient.class);

	public ScanResultPanel(final String scanId, final ScreenBeanDescriptor screenBeanDescriptor) {
		scripWrapperTableView = createScripWrapperTableView(screenBeanDescriptor);
		progressBar = createInformativeProgressBar();
		scanResultPollingTask = createScanResultPollingTask(scanId);
		progressInformationNode = createProgressInformationNode(progressBar, scanId);
		getChildren().setAll(createScreenConfigurationInfoPane(screenBeanDescriptor), scripWrapperTableView, progressInformationNode);

		Constant.timer.schedule(scanResultPollingTask, DELAY, DELAY);
	}

	private Node createScreenConfigurationInfoPane(final ScreenBeanDescriptor screenBeanDescriptor) {
		final TitledPane titledPane = new TitledPane();
		titledPane.setText(screenBeanDescriptor.getDisplayName() + " " + MessageSource.get("Configuration"));
		titledPane.setExpanded(false);
		titledPane.setAnimated(false);
		titledPane.expandedProperty().addListener(new ChangeListener<Boolean>() {

			private VBox pane;

			@Override
			public void changed(final ObservableValue<? extends Boolean> observable, final Boolean oldValue, final Boolean newValue) {
				if (newValue && (null == pane)) {
					pane = UI.style(new VBox(), CSS.beanAutoUIReadOnly);
					pane.setMaxWidth(Double.MAX_VALUE);
					for (final PropertyDescriptor propertyDescriptor : screenBeanDescriptor.getPropertyDescriptors()) {
						final Label label = UI.style(new Label(propertyDescriptor.getDisplayName() + " : " + String.valueOf(propertyDescriptor.getValue())), CSS.buttonMockingLabel);
						label.setMaxWidth(Double.MAX_VALUE);
						pane.getChildren().add(label);
					}
					titledPane.setContent(pane);
				}
			}
		});
		return titledPane;
	}

	private ScripWrapperTableView createScripWrapperTableView(final ScreenBeanDescriptor screenBeanDescriptor) {
		final ScripWrapperTableView scripWrapperTableView = new ScripWrapperTableView(screenBeanDescriptor);
		scripWrapperTableView.setContextMenu(new ScanResultContextMenu(scripWrapperTableView));
		VBox.setVgrow(scripWrapperTableView, Priority.ALWAYS);
		return scripWrapperTableView;
	}

	public ScripWrapperTableView getScripWrapperTableView() {
		return scripWrapperTableView;
	}

	private InformativeProgressBar createInformativeProgressBar() {
		final InformativeProgressBar progressBar = new InformativeProgressBar();
		progressBar.setMaxWidth(Double.MAX_VALUE);
		progressBar.setProgress(0);
		return progressBar;
	}

	private Node createProgressInformationNode(final InformativeProgressBar progressBar, final String scanId) {
		final Button button = UI.mediumCrossButton(MessageSource.get("Cancel"));
		button.setOnAction(new EventHandler<ActionEvent>() {
			@Override
			public void handle(final ActionEvent event) {
				cancel(scanId);
			}
		});
		final HBox hbox = new HBox(progressBar, button);
		UI.style(hbox, CSS.screenProgressInfoPane);
		HBox.setHgrow(progressBar, Priority.ALWAYS);
		hbox.setMaxWidth(Double.MAX_VALUE);
		hbox.backgroundProperty().bind(scripWrapperTableView.backgroundProperty());
		return hbox;
	}

	public void cancel(final String scanId) {
		scanResultPollingTask.cancel();
		screenerClient.cancel(scanId);
	}

	private void processScanCompletion() {
		scanResultPollingTask.cancel();
		StoxPlatform.runLater(new Runnable() {
			@Override
			public void run() {
				getChildren().remove(progressInformationNode);
			}
		});
	}

	private void updateScanStatus(final String scanId) {
		final double completionPercentage = screenerClient.completionPercentage(scanId);
		progressBar.setProgress(completionPercentage / 100);
		if (100 == completionPercentage) {
			processScanCompletion();
		}
	}

	private void updateScanResults(final String scanId) {
		final List<BeanDescriptor> beanDescriptors = screenerClient.getMatches(scanId);
		if ((null != beanDescriptors) && !beanDescriptors.isEmpty()) {
			scripWrapperTableView.getBeanDescriptors().addAll(beanDescriptors);
			for (final BeanDescriptor beanDescriptor : beanDescriptors) {
				for (final PropertyDescriptor propertyDescriptor : beanDescriptor.getPropertyDescriptors()) {
					if ("ScripId".equalsIgnoreCase(propertyDescriptor.getBaseName().trim())) {
						final Long scripId = Long.parseLong(String.valueOf(propertyDescriptor.getValue()));
						if ((null != scripId) && (0 != scripId)) {
							scripWrapperTableView.getItems().add(new ScripWrapper(scripClient.findOne(scripId)));
						}
					}
				}
			}
		}
	}

	private TimerTask createScanResultPollingTask(final String scanId) {
		return new TimerTask() {

			private boolean processing;

			@Override
			public void run() {
				if (!processing) {
					try {
						processing = true;
						updateScanStatus(scanId);
						updateScanResults(scanId);
					} finally {
						processing = false;
					}
				}
			}
		};
	}

}
